# Copyright (c) 2018 Presto Labs Pte. Ltd.
# Author: xguo

import dateutil.parser

import coin.exchange.base.kr_rest.level_book as level_book_mdl
import coin.exchange.base.kr_rest.public_client_base as pubcb
import coin.exchange.hitbtc_v2.kr_rest.native_public_client as npubc
from coin.exchange.hitbtc_v2.kr_rest.product import HitbtcProduct

from coin.proto.coin_market_query_pb2 import ProductTicker, ExchangeTicker


class HitbtcFeedParser(object):
  @staticmethod
  def _check_ticker(ticker):
    important_keys = set(['ask', 'bid', 'last', 'low', 'high', 'volume', 'timestamp', 'symbol'])
    assert important_keys <= set(ticker.keys())
    # Some ticker's bid is null, which implies that the product is not
    # actively traded, we should ignore such ticker.
    assert None not in ticker.values()

  @staticmethod
  def parse_native_ticker(update_msg, product):
    """ sample response message
    {
      "ask": "8335.27",
      "bid": "8330.30",
      "last": "8334.26",
      "open": "9874.97",
      "low": "8330.64",
      "high": "9932.27",
      "volume": "8720.01",
      "volumeQuote": "79432710.5746",
      "timestamp": "2018-02-02T09:21:10.999Z",
      "symbol": "BTCUSD"
    }"""
    assert product.native_symbol == update_msg['symbol']
    timestamp = int(dateutil.parser.parse(update_msg['timestamp']).timestamp() * 1e+9)
    return ProductTicker(ask=float(update_msg['ask']),
                         bid=float(update_msg['bid']),
                         last=float(update_msg['last']),
                         high=float(update_msg['high']),
                         low=float(update_msg['low']),
                         volume_24hr=float(update_msg['volume']),
                         exchange_timestamp=timestamp,
                         native_symbol=product.native_symbol,
                         symbol=product.symbol)

  @staticmethod
  def parse_exchange_ticker(update_msg: list) -> ExchangeTicker:
    product_ticker_list = []
    for ticker in update_msg:
      try:
        product = HitbtcProduct.FromStr(ticker['symbol'])
        HitbtcFeedParser._check_ticker(ticker)
      except Exception:
        continue  # Ignore unknown product or not actively traded product.
      product_ticker_list.append(HitbtcFeedParser.parse_native_ticker(ticker, product))
    assert len(product_ticker_list) > 0
    return ExchangeTicker(exchange='Hitbtc', each_ticker=product_ticker_list)

  @staticmethod
  def parse_level_book(update_msg, product):
    """ sample response message
    {
      "ask": [
        { "price": "8552.04", "size": "0.20" },
        { "price": "8552.05", "size": "1.19" },
        { "price": "8552.06", "size": "1.08" },
        .....
      ],
      "bid": [
        { "price": "8545.65", "size": "0.02" },
        { "price": "8545.64", "size": "1.89" },
        { "price": "8545.63", "size": "0.10" },
        .....
      ]
    } """
    level_book = level_book_mdl.LevelBook(product)
    # Hitbtc's book depth = 100
    for elem in update_msg['ask']:
      level_elem = level_book_mdl.LevelElement(float(elem['price']), float(elem['size']), 0)
      level_book.add_ask(level_elem)

    for elem in update_msg['bid']:
      level_elem = level_book_mdl.LevelElement(float(elem['price']), float(elem['size']), 0)
      level_book.add_bid(level_elem)
    level_book.finalize()
    return level_book


class HitbtcPublicClient(pubcb.PublicClientBase):
  def __init__(self, timeout=10):
    self.npubc = npubc.HitbtcNativePublicClient(timeout=timeout)

  def query_ticker_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.get_tickers(symbol)
    update.msg = HitbtcFeedParser.parse_native_ticker(update.msg, product)
    return update

  def query_exchange_ticker_impl(self):
    update = self.npubc.get_tickers()
    update.msg = HitbtcFeedParser.parse_exchange_ticker(update.msg)
    return update

  def query_level_book_impl(self, product):
    symbol = product.native_symbol
    update = self.npubc.get_orderbook(symbol)
    update.msg = HitbtcFeedParser.parse_level_book(update.msg, product)
    return update
